#ifndef __EPSILON__FILE_H__
#define __EPSILON__FILE_H__

#include "string.h"
#include <stdio.h>
#include <time.h>

// For our purposes, 32-bit file offsets are fine.
typedef unsigned long  eFileSize;
typedef long           eFileOffset;
typedef int            eFileUser;
typedef int            eFileGroup;

//typedef long   time_t;

// Convenient shorthand names
#define eFileType     Epsilon::File::Types
#define eFileSeekOp   Epsilon::File::SeekOperations
#define eFileMode     Epsilon::File::Modes
#define eFileLockMode Epsilon::File::LockModes
#define eFileStat     Epsilon::File::Stats

namespace Epsilon {
  namespace File {

    namespace Types {
      enum Type {
        Regular,
        Directory,
      };
    };

    namespace SeekOperations {
      enum Operation {
        Set,
        Current,
        End,
      };
    };

    namespace LockModes {
      enum Lock {
        Shared,
        Exclusive,
        Unlock
      };
    };

    namespace Modes {
      enum Flags {
        Read       = 0x0001,
        Write      = 0x0002,
        ReadWrite  = (Read | Write),
        Append     = 0x0100,
        Create     = 0x0200,
        Truncate   = 0x0400,
        Exclusive  = 0x0800, ///< Locks the file
        Binary     = 0x1000,
        Sequential = 0x2000,

        Invalid    = -1
      };

      class Mode {
      public:
        Mode (void)            : flags (Invalid) {};
        Mode (const int flags) : flags (flags)   {};

        Mode (const TCHAR* szMode);

        operator int (void) {
          return flags;
        }

      private:
        int flags;
      };
    };

    namespace Stats {
      class Stat {
      public:
      private:
        eFileSize        size;
        eFileType::Type  type;
        eFileMode::Mode  mode;

        eFileUser        user;
        eFileGroup       group;

        struct {
          time_t         accessed;
          time_t         modified;
          time_t         changed;
        } file_times;
      };
    };
  };
};

/// XXX: Only accepts file names in ANSI for the time-being.

// Files larger than 4 Gb are unsupported, by design.
class eFile {
public:
           eFile (void) { };
  virtual ~eFile (void) { };

  virtual bool open  (eStringA& name, eFileMode::Mode mode) = 0;
  virtual bool close (void)                                = 0;

  virtual bool        seek   (eFileOffset offset, eFileSeekOp::Operation seek_op) = 0;
  virtual void        rewind (void)                                               = 0;
  virtual eFileOffset tell   (void)                                               = 0;
  virtual eFileSize   size   (void)                                               = 0;

  virtual eFileSize read  (void* pOut,       eFileSize element_size, int num_elements = 1) = 0;
  virtual eFileSize write (const void* pBuf, eFileSize element_size, int num_elements = 1) = 0;

  virtual bool lock   (eFileLockMode::Lock lock_mode) = 0;
  virtual bool unlock (void)                          = 0;

private:
//  eFileMode::Mode mode;
};

class eDiskFile : public eFile {
public:
           eDiskFile (void) : fh   (NULL),
                               mode (eFileMode::Invalid) { };
  virtual ~eDiskFile (void) { close (); };

  virtual bool open  (eStringA& name, eFileMode::Mode mode);
  virtual bool close (void);

  virtual bool        seek   (eFileOffset offset, eFileSeekOp::Operation seek_op);
  virtual void        rewind (void);
  virtual eFileOffset tell   (void);
  virtual eFileSize   size   (void);


  virtual eFileSize read  (void*       pBuf, eFileSize element_size, int num_elements = 1);
  virtual eFileSize write (const void* pBuf, eFileSize element_size, int num_elements = 1);

  virtual bool lock   (eFileLockMode::Lock lock_mode);
  virtual bool unlock (void) { return lock (eFileLockMode::Unlock); };

private:
  FILE*           fh;
  eFileMode::Mode mode;
};

#ifdef WIN32
#include <windows.h>

class eMemoryMappedFile : public eFile {
public:
           eMemoryMappedFile (void) : mode (eFileMode::Invalid) { };
  virtual ~eMemoryMappedFile (void) { close (); };

  virtual bool open  (eStringA& name, eFileMode::Mode mode);
  virtual bool close (void);

  virtual bool        seek   (eFileOffset offset, eFileSeekOp::Operation seek_op);
  virtual void        rewind (void);
  virtual eFileOffset tell   (void);
  virtual eFileSize   size   (void);


  virtual eFileSize read  (void*       pBuf, eFileSize element_size, int num_elements = 1);
  virtual eFileSize write (const void* pBuf, eFileSize element_size, int num_elements = 1);

  virtual bool lock   (eFileLockMode::Lock lock_mode);
  virtual bool unlock (void) { return lock (eFileLockMode::Unlock); };

private:
  eFileOffset     pos;
  HANDLE          fh;
  HANDLE          mapped_file;
  DWORD           file_size;
  void*           view_base;
  eFileMode::Mode mode;
};
#endif

/*
class eCompressedFile : public eFile {
public:

//  TYPES: ZIP
//         ECF (Epsilon Compression File)

           eCompressedFile (void) : fh   (NULL),
                                    mode (eFileMode::Invalid) { };
  virtual ~eCompressedFile (void) { close (); };

  virtual bool open  (const TCHAR* szName, eFileMode::Mode mode);
  virtual bool close (void);

  virtual bool        seek   (eFileOffset offset, eFileSeekOp::Operation seek_op);
  virtual void        rewind (void);
  virtual eFileOffset tell   (void);
  virtual eFileSize   size   (void);


  virtual eFileSize read  (void*       pBuf, eFileSize element_size, int num_elements = 1);
  virtual eFileSize write (const void* pBuf, eFileSize element_size, int num_elements = 1);

  virtual bool lock   (eFileLockMode::Lock lock_mode);
  virtual bool unlock (void) { return lock (eFileLockMode::Unlock); };

private:
  FILE*           fh;
  eFileMode::Mode mode;
};
*/

/*
class eNetworkFile : public eFile {
public:

//  TYPES: HTTP
//         FTP (Reserved for Future Use)

           eCompressedFile (void) : mode (eFileMode::Invalid) { };
  virtual ~eCompressedFile (void) { close (); };

  virtual bool open  (const TCHAR* szName, eFileMode::Mode mode);
  virtual bool close (void);

  virtual bool        seek   (eFileOffset offset, eFileSeekOp::Operation seek_op);
  virtual void        rewind (void);
  virtual eFileOffset tell   (void);
  virtual eFileSize   size   (void);


  virtual eFileSize read  (void*       pBuf, eFileSize element_size, int num_elements = 1);
  virtual eFileSize write (const void* pBuf, eFileSize element_size, int num_elements = 1);

  virtual bool lock   (eFileLockMode::Lock lock_mode);
  virtual bool unlock (void) { return lock (eFileLockMode::Unlock); };

private:
  eFileMode::Mode mode;
};
*/

#endif /* __EPSILON__FILE_H__ */
